package com.adopapa.wechatapi.application.service;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.Map;

import org.apache.log4j.Logger;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;

import com.adopapa.wechatapi.domain.common.ResultMessage;
import com.adopapa.wechatapi.domain.media.DownloadMediaResult;
import com.adopapa.wechatapi.domain.media.UploadMediaResult;
import com.adopapa.wechatapi.domain.menu.Menu;
import com.adopapa.wechatapi.domain.message.customer.CustomerServiceMessage;
import com.adopapa.wechatapi.domain.message.mass.GroupMessage;
import com.adopapa.wechatapi.domain.message.mass.MpNews;
import com.adopapa.wechatapi.domain.message.mass.MpVideo;
import com.adopapa.wechatapi.domain.message.mass.OpenIdMessage;
import com.adopapa.wechatapi.domain.message.template.SubscribeMessage;
import com.adopapa.wechatapi.domain.message.template.TemplateMessage;
import com.adopapa.wechatapi.domain.pay.CloseRequest;
import com.adopapa.wechatapi.domain.pay.CloseResponse;
import com.adopapa.wechatapi.domain.pay.RedpacketRequest;
import com.adopapa.wechatapi.domain.pay.RedpacketResult;
import com.adopapa.wechatapi.domain.pay.RefundRequest;
import com.adopapa.wechatapi.domain.pay.RefundResponse;
import com.adopapa.wechatapi.domain.pay.UnifiedOrder;
import com.adopapa.wechatapi.domain.pay.UnifiedResult;
import com.adopapa.wechatapi.domain.token.AccessToken;
import com.adopapa.wechatapi.domain.token.JsApiTicket;
import com.adopapa.wechatapi.domain.token.OAuthUserInfo;
import com.adopapa.wechatapi.domain.token.QrCode;
import com.adopapa.wechatapi.domain.token.WebToken;
import com.adopapa.wechatapi.util.ApiUrls;
import com.adopapa.wechatapi.util.JsonUtil;
import com.adopapa.wechatapi.util.MessageUtil;
import com.adopapa.wechatapi.util.WeChatUtil;
import com.alibaba.fastjson.JSONObject;

public class WeChatApi {

	private final static Logger LOGGER = Logger.getLogger(WeChatApi.class);

	/**
	 * 获取access token
	 */
	public static AccessToken getAccessToken(String appId, String appSecret)
			throws JsonParseException, JsonMappingException, IOException {
		String url = MessageFormat.format(ApiUrls.ACCESS_TOKEN_URL, appId, appSecret);
		String result = WeChatUtil.httpsRequest(url, "POST", null);
		if (result != null) {
			// result = result.replace("access_token",
			// "token").replace("expires_in", "expiresIn");
			AccessToken token = JsonUtil.json2Bean(result, AccessToken.class);
			return token;
		}
		return null;

	}

	// 参数 是否必须 说明
	// access_token 是 调用接口凭证
	// openid 是 普通用户的标识，对当前公众号唯一
	// lang 否 返回国家地区语言版本，zh_CN 简体，zh_TW 繁体，en 英语
	public static OAuthUserInfo getUserInfo(String accessToken, String openId, String language) throws Exception {
		String url = MessageFormat.format(ApiUrls.GET_USER_INFO, accessToken, openId, language);
		LOGGER.info(url);
		String result = WeChatUtil.httpsRequest(url, "GET", null);
		LOGGER.info(result);
		if (result != null) {
			result = result.replace("msg_id", "msgId");
			result = result.replace("subscribe_time", "subscribeTime");
			OAuthUserInfo userInfo = JsonUtil.json2Bean(result, OAuthUserInfo.class);
			return userInfo;
		}
		return null;
	}

	public static ResultMessage checkWebAccessToken(String accessToken, String openId) throws Exception {
		String url = MessageFormat.format(ApiUrls.CHECK_WEB_ACCESSTOKEN, accessToken, openId);
		LOGGER.info(url);
		String result = WeChatUtil.httpsRequest(url, "GET", null);
		LOGGER.info(result);
		if (result != null) {
			result = result.replace("msg_id", "msgId");
			ResultMessage resultMessage = JsonUtil.json2Bean(result, ResultMessage.class);
			return resultMessage;
		}
		return null;
	}

	/**
	 * 创建菜单
	 */
	public static ResultMessage createMenu(String accessToken, Menu menu) throws IOException {
		String menuJson = JsonUtil.bean2Json(menu);
		menuJson = menuJson.replace("buttons", "button").replaceAll("subButtons", "sub_button");
		LOGGER.info(menuJson);
		String url = MessageFormat.format(ApiUrls.MENU_CREATE_URL, accessToken);
		String result = WeChatUtil.httpsRequest(url, "POST", menuJson);
		if (result != null) {
			result = result.replace("msg_id", "msgId");
			ResultMessage resultMessage = JsonUtil.json2Bean(result, ResultMessage.class);
			return resultMessage;
		}
		return null;
	}

	/**
	 * 查询菜单 {"menu":{"button":[{"type":"click","name":"今日歌曲","key":
	 * "V1001_TODAY_MUSIC" ,"sub_button":[]},{"type":"click","name":"歌手简介","key":
	 * "V1001_TODAY_SINGER"
	 * ,"sub_button":[]},{"name":"菜单","sub_button":[{"type":"view"
	 * ,"name":"搜索","url" :"http://www.soso.com/","sub_button":[]},{"type":"view",
	 * "name":"视频","url": "http://v.qq.com/","sub_button":[]},{"type":"click","name"
	 * :"赞一下我们","key":"V1001_GOOD","sub_button":[]}]}]}}
	 */
	public static Menu getMenu(String accessToken) throws JsonParseException, JsonMappingException, IOException {
		String url = MessageFormat.format(ApiUrls.MENU_GET_URL, accessToken);
		String result = WeChatUtil.httpsRequest(url, "GET", null);
		if (result != null) {
			result = result.replace("msg_id", "msgId");
			Menu menu = JsonUtil.json2Bean(result, Menu.class);
			return menu;
		}
		return null;
	}

	/**
	 * 删除菜单 http请求方式：GET https://api.weixin.qq.com/cgi-bin/menu/delete?access_token
	 * =ACCESS_TOKEN
	 */
	public static ResultMessage deleteMenu(String accessToken)
			throws JsonParseException, JsonMappingException, IOException {
		String url = MessageFormat.format(ApiUrls.MENU_DELETE_URL, accessToken);
		String result = WeChatUtil.httpsRequest(url, "GET", null);
		if (result != null) {
			result = result.replace("msg_id", "msgId");
			ResultMessage resultMessage = JsonUtil.json2Bean(result, ResultMessage.class);
			return resultMessage;
		}
		return null;
	}

	/**
	 * 修改备注 { "openid":"oDF3iY9ffA-hqb2vVvbr7qxf6A0Q", "remark":"pangzi" }
	 */
	// access_token 调用接口凭证
	// openid 用户标识
	// remark 新的备注名，长度必须小于30字符
	// {
	// "errcode":0,
	// "errmsg":"ok"
	// }
	// {"errcode":40013,"errmsg":"invalid appid"}
	public static ResultMessage updateRemark(String accessToken, String remarkJson)
			throws JsonParseException, JsonMappingException, IOException {
		String url = MessageFormat.format(ApiUrls.UPDATE_REMARK, accessToken);
		String result = WeChatUtil.httpsRequest(url, "POST", remarkJson);
		if (result != null) {
			result = result.replace("msg_id", "msgId");
			ResultMessage resultMessage = JsonUtil.json2Bean(result, ResultMessage.class);
			return resultMessage;
		}
		return null;

	}

	/**
	 * 第三方网站使用微信登录获取code appid 是 应用唯一标识 redirect_uri 是 重定向地址，需要进行UrlEncode
	 * response_type 是 填code scope 是 应用授权作用域，拥有多个作用域用逗号（,）分隔，网页应用目前仅填写snsapi_login即可
	 * state 否 用于保持请求和回调的状态，授权请求后原样带回给第三方
	 * 。该参数可用于防止csrf攻击（跨站请求伪造攻击），建议第三方带上该参数，可设置为简单的随机数加session进行校验
	 */
	public static String getOAuth2Url4PC(String appId, String redirectUrl, String responseType, String scope,
			String state) {
		return MessageFormat.format(ApiUrls.SCOPE_QRCODE_URL, appId, redirectUrl, responseType, scope, state);
	}

	/**
	 * 1、构造获取code url
	 */
	// Scope为snsapi_base
	// https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx520c15f417810387
	// &redirect_uri=http%3A%2F%2Fchong.qq.com%2Fphp%2Findex.php%3Fd%3D%26c%3DwxAdapter%26m%3DmobileDeal%26showwxpaytitle%3D1%26vb2ctag%3D4_2030_5_1194_60
	// &response_type=code&scope=snsapi_base&state=123#wechat_redirect
	// Scope为snsapi_userinfo
	// https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxf0e81c3bee622d60
	// &redirect_uri=http%3A%2F%2Fnba.bluewebgame.com%2Foauth_response.php
	// &response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect
	public static String getOAuth2Url(String appId, String redirectUrl, String responseType, String scope,
			String state) {
		// appid 是 公众号的唯一标识
		// redirect_uri 是 授权后重定向的回调链接地址，请使用urlencode对链接进行处理
		// response_type 是 返回类型，请填写code
		// scope 是 应用授权作用域，snsapi_base （不弹出授权页面，直接跳转，只能获取用户openid），
		// snsapi_userinfo
		// （弹出授权页面，可通过openid拿到昵称、性别、所在地。并且，即使在未关注的情况下，只要用户授权，也能获取其信息）
		// state 否 重定向后会带上state参数，开发者可以填写a-zA-Z0-9的参数值，最多128字节
		// #wechat_redirect 是 无论直接打开还是做页面302重定向时候，必须带此参数
		return MessageFormat.format(ApiUrls.SCOPE_CODE_URL, appId, redirectUrl, responseType, scope, state);

	}

	/**
	 * 2、获取accessToken
	 */
	// appid 是 公众号的唯一标识
	// secret 是 公众号的appsecret
	// code 是 填写第一步获取的code参数
	// grant_type 是 填写为authorization_code
	// {
	// "access_token":"ACCESS_TOKEN",
	// "expires_in":7200,
	// "refresh_token":"REFRESH_TOKEN",
	// "openid":"OPENID",
	// "scope":"SCOPE"
	// }
	public static WebToken getWebToken(String appId, String appSecret, String code, String grantType)
			throws JsonParseException, JsonMappingException, IOException {
		String url = MessageFormat.format(ApiUrls.WEB_TOKEN_URL, appId, appSecret, code, "authorization_code");
		String result = WeChatUtil.httpsRequest(url, "POST", null);
		if (result != null) {
			// private String accessToken;
			// private int expiresIn;
			// private String refreshToken;
			// private String openId;
			result = result.replaceAll("access_token", "accessToken").replaceAll("expires_in", "expiresIn");
			result = result.replaceAll("refresh_token", "refreshToken").replaceAll("openid", "openId");
			WebToken webToken = JsonUtil.json2Bean(result, WebToken.class);
			return webToken;
		}
		return null;
	}

	/**
	 * 3、刷新token 由于access_token拥有较短的有效期，当access_token超时后，可以使用refresh_token进行刷新，
	 * refresh_token拥有较长的有效期（7天、30天、60天、90天），当refresh_token失效的后，需要用户重新授权。
	 */
	// appid 是 公众号的唯一标识
	// grant_type 是 填写为refresh_token
	// refresh_token 是 填写通过access_token获取到的refresh_token参数
	public static WebToken refreshWebToken(String appId, String refreshToken, String grantType)
			throws JsonParseException, JsonMappingException, IOException {
		String url = MessageFormat.format(ApiUrls.REFRESH_TOKEN_URL, appId, "refresh_token", refreshToken);
		String result = WeChatUtil.httpsRequest(url, "POST", null);
		if (result != null) {
			WebToken webToken = JsonUtil.json2Bean(result, WebToken.class);
			return webToken;
		}
		return null;
	}

	/**
	 * 4、获取用户信息 如果网页授权作用域为snsapi_userinfo，则此时开发者可以通过access_token和openid拉取用户信息了。
	 */
	// access_token 网页授权接口调用凭证,注意：此access_token与基础支持的access_token不同
	// openid 用户的唯一标识
	// lang 返回国家地区语言版本，zh_CN 简体，zh_TW 繁体，en 英语
	public static OAuthUserInfo getWeChatUserInfo(String webToken, String openId, String language)
			throws JsonParseException, JsonMappingException, IOException {
		String url = MessageFormat.format(ApiUrls.GET_WEB_USER_INFO_URL, webToken, openId, language);
		String result = WeChatUtil.httpsRequest(url, "GET", null);
		LOGGER.info(result);
		if (result != null) {
			OAuthUserInfo weChatUserInfo = JsonUtil.json2Bean(result, OAuthUserInfo.class);
			return weChatUserInfo;
		}
		return null;
	}

	/**
	 * 创建二维码ticket
	 */
	// 每次创建二维码ticket需要提供一个开发者自行设定的参数（scene_id），分别介绍临时二维码和永久二维码的创建二维码ticket过程。
	// 临时二维码请求说明
	// http请求方式: POST
	// URL:
	// https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN
	// POST数据格式：json
	// POST数据例子：{"expire_seconds": 1800, "action_name": "QR_SCENE",
	// "action_info": {"scene": {"scene_id": 123}}}
	// 永久二维码请求说明
	// http请求方式: POST
	// URL:
	// https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN
	// POST数据格式：json
	// POST数据例子：{"action_name": "QR_LIMIT_SCENE", "action_info": {"scene":
	// {"scene_id": 123}}}
	// 或者也可以使用以下POST数据创建字符串形式的二维码参数：
	// {"action_name": "QR_LIMIT_STR_SCENE", "action_info": {"scene":
	// {"scene_str": "123"}}}
	// expire_seconds 该二维码有效时间，以秒为单位。 最大不超过1800。
	// action_name
	// 二维码类型，QR_SCENE为临时,QR_LIMIT_SCENE为永久,QR_LIMIT_STR_SCENE为永久的字符串参数值
	// action_info 二维码详细信息
	// scene_id 场景值ID，临时二维码时为32位非0整型，永久二维码时最大值为100000（目前参数只支持1--100000）
	// scene_str 场景值ID（字符串形式的ID），字符串类型，长度限制为1到64，仅永久二维码支持此字段
	public static QrCode createIntQrCode(Integer sceneId, Integer seconds, String accessToken)
			throws JsonParseException, JsonMappingException, IOException {
		JSONObject jsonObject = new JSONObject();
		if (sceneId == null || sceneId <= 0) {
			sceneId = 1;
		}
		String actionName = null;
		if (seconds == null || seconds == 0) {
			actionName = "QR_LIMIT_SCENE";
			if (sceneId > 100000) {
				sceneId = 100000;
			}
		} else {
			actionName = "QR_SCENE";
			jsonObject.put("expire_seconds", seconds);
			if (seconds > 2592000) {
				seconds = 2592000;
			}
			if (seconds <= 0) {
				seconds = 0;
			}
		}
		JSONObject jsonObject2 = new JSONObject();
		JSONObject jsonObject3 = new JSONObject();
		jsonObject3.put("scene_id", sceneId + "");
		jsonObject2.put("scene", jsonObject3);
		jsonObject.put("action_name", actionName);
		jsonObject.put("action_info", jsonObject2);

		String url = MessageFormat.format(ApiUrls.CREATE_QRCODE_URL, accessToken);
		String result = WeChatUtil.httpsRequest(url, "POST", jsonObject.toJSONString());
		if (result != null) {
			result = result.replace("expire_seconds", "expires");
			QrCode qrCode = JsonUtil.json2Bean(result, QrCode.class);
			return qrCode;
		}
		return null;
	}

	public static QrCode createStrQrCode(String sceneStr, Integer seconds, String accessToken)
			throws JsonParseException, JsonMappingException, IOException {
		JSONObject jsonObject = new JSONObject();
		if (sceneStr == null) {
			sceneStr = "";
		}
		String actionName = null;
		if (seconds == null || seconds == 0) {
			actionName = "QR_LIMIT_STR_SCENE";
		} else {
			actionName = "QR_STR_SCENE";
			jsonObject.put("expire_seconds", seconds);
			if (seconds > 2592000) {
				seconds = 2592000;
			}
			if (seconds <= 0) {
				seconds = 0;
			}
		}
		JSONObject jsonObject2 = new JSONObject();
		JSONObject jsonObject3 = new JSONObject();
		jsonObject3.put("scene_str", sceneStr);
		jsonObject2.put("scene", jsonObject3);
		jsonObject.put("action_name", actionName);
		jsonObject.put("action_info", jsonObject2);

		String url = MessageFormat.format(ApiUrls.CREATE_QRCODE_URL, accessToken);
		String result = WeChatUtil.httpsRequest(url, "POST", jsonObject.toJSONString());
		if (result != null) {
			result = result.replace("expire_seconds", "expires");
			QrCode qrCode = JsonUtil.json2Bean(result, QrCode.class);
			return qrCode;
		}
		return null;
	}

	/**
	 * 通过ticket换取二维码 获取二维码ticket后，开发者可用ticket换取二维码图片。请注意，本接口无须登录态即可调用。
	 */
	public static String getQrCode(String ticket) {
		return MessageFormat.format(ApiUrls.SHOW_QRCODE_URL, ticket);
	}

	public static ResultMessage sendCustomerServiceMessage(CustomerServiceMessage message, String accessToken)
			throws IOException {
		String url = MessageFormat.format(ApiUrls.CUSTOMER_MESSAGE_SEND_URL, accessToken);
		String messageJson = JsonUtil.bean2Json(message);
		messageJson = messageJson.replaceAll("picUrl", "picurl").replaceAll("mediaId", "media_id")
				.replaceAll("musicUrl", "musicurl");
		messageJson = messageJson.replaceAll("hqMusicUrl", "hqmusicurl").replaceAll("thumbMediaId", "thumb_media_id");
		messageJson = messageJson.replaceAll("msgType", "msgtype").replaceAll("toUser", "touser");

		LOGGER.info(message);

		String result = WeChatUtil.httpsRequest(url, "POST", messageJson);

		if (result != null) {
			result = result.replace("msg_id", "msgId");
			ResultMessage resultMessage = JsonUtil.json2Bean(result, ResultMessage.class);
			return resultMessage;
		}
		return null;

	}

	/**
	 * 上传多媒体文件到微信服务器 媒体文件类型，分别有图片（image）、语音（voice）、视频（video）和缩略图（thumb）
	 */
	public static UploadMediaResult uploadMedia(String accessToken, File file, String mediaType) throws Exception {

		String url = MessageFormat.format(ApiUrls.UPLOAD_MEDIA_FILE_URL, accessToken, mediaType);

		String result = WeChatUtil.upload(url, file);
		// String result =
		// "{\"type\":\"image\",\"media_id\":\"3TRxgvjJjKxDza5PkcRNyvJPmUEGngRU3LzEeWSNVHKg7rdRpj4LDZKZ6TWrcb1k\",\"created_at\":1423892491}";
		if (result != null) {
			// result = result.replace("media_id",
			// "mediaId").replace("created_at", "createAt");
			UploadMediaResult uploadMediaResult = JsonUtil.json2Bean(result, UploadMediaResult.class);
			return uploadMediaResult;
		}

		return null;
	}

	/**
	 * 从微信服务器下载多媒体文件，该方法将文件数据缓存到内存中不做进一步处理， 因此当从附件对象中读取数据结束后应该迅速将附件对象置空。
	 */
	public static DownloadMediaResult downMedia(String accessToken, String mediaId) throws Exception {
		String url = MessageFormat.format(ApiUrls.GET_MEDIA_FILE_URL, accessToken, mediaId);
		DownloadMediaResult downloadMediaResult = WeChatUtil.downloadMedia(url);
		return downloadMediaResult;
	}

	/**
	 * 上传图文消息素材【订阅号与服务号认证后均可用】
	 */
	public static UploadMediaResult uploadNews(String accessToken, MpNews mpNews) throws IOException {
		String url = MessageFormat.format(ApiUrls.UPLOAD_NEWS_URL, accessToken);
		String result = WeChatUtil.httpsRequest(url, "POST", mpNews.toJson());
		if (result != null) {
			// result = result.replace("media_id",
			// "mediaId").replace("created_at", "createAt");
			UploadMediaResult uploadMediaResult = JsonUtil.json2Bean(result, UploadMediaResult.class);
			return uploadMediaResult;
		}

		return null;
	}

	/**
	 * 群发视频时，需要再次上传mediaId，获取新的mediaId
	 */
	public static UploadMediaResult uploadMpVideo(String accessToken, MpVideo mpVideo) throws IOException {
		String url = MessageFormat.format(ApiUrls.UPLOAD_VIDEO_URL, accessToken);
		String result = WeChatUtil.httpsRequest(url, "POST", mpVideo.toJson());
		if (result != null) {
			// result = result.replace("media_id",
			// "mediaId").replace("created_at", "createAt");
			UploadMediaResult uploadMediaResult = JsonUtil.json2Bean(result, UploadMediaResult.class);
			return uploadMediaResult;
		}
		return null;

	}

	/**
	 * 根据分组进行群发【订阅号与服务号认证后均可用】
	 */
	public static ResultMessage sendGroupMessage(String accessToken, GroupMessage groupMessage) throws IOException {
		String url = MessageFormat.format(ApiUrls.MASS_MESSAGE_SENDALL, accessToken);
		String result = WeChatUtil.httpsRequest(url, "POST", groupMessage.toJson());
		if (result != null) {
			result = result.replace("msg_id", "msgId");
			ResultMessage resultMessage = JsonUtil.json2Bean(result, ResultMessage.class);
			return resultMessage;
		}
		return null;
	}

	/**
	 * 根据OpenID列表群发【订阅号不可用，服务号认证后可用】
	 */
	public static ResultMessage sendOpenIdMessage(String accessToken, OpenIdMessage openIdMessage) throws IOException {
		String url = MessageFormat.format(ApiUrls.MASS_MESSAGE_SEND, accessToken);
		String result = WeChatUtil.httpsRequest(url, "POST", openIdMessage.toJson());
		if (result != null) {
			result = result.replace("msg_id", "msgId");
			ResultMessage resultMessage = JsonUtil.json2Bean(result, ResultMessage.class);
			return resultMessage;
		}
		return null;
	}

	/**
	 * 模板消息发送
	 */
	public static ResultMessage sendTemplateMessage(String accessToken, TemplateMessage templateMessage)
			throws IOException {
		String url = MessageFormat.format(ApiUrls.TEMPLATE_MESSAGE_SEND_URL, accessToken);
		String result = WeChatUtil.httpsRequest(url, "POST", templateMessage.toJson());
		if (result != null) {
			result = result.replace("msg_id", "msgId");
			ResultMessage resultMessage = JsonUtil.json2Bean(result, ResultMessage.class);
			return resultMessage;
		}
		return null;
	}

	/**
	 * 获取jsApiTicket,类似accessToken
	 */
	public static JsApiTicket getJsApiTicket(String accessToken)
			throws JsonParseException, JsonMappingException, IOException {
		String url = MessageFormat.format(ApiUrls.GET_JSAPI_TICKET_URL, accessToken);
		String result = WeChatUtil.httpsRequest(url, "GET", null);
		if (result != null) {
			// result = result.replace("expires_in", "expiresIn");
			JsApiTicket ticket = JsonUtil.json2Bean(result, JsApiTicket.class);
			return ticket;
		}
		return null;
	}

	/**
	 * 统一下单
	 * 
	 * @param unifiedOrder
	 * @return
	 * @throws Exception
	 */
	public static UnifiedResult sendOrder(UnifiedOrder unifiedOrder) throws Exception {
		String url = ApiUrls.UNIFIED_ORDER_URL;
		System.out.println(unifiedOrder.toXml());
		String result = WeChatUtil.httpsRequest(url, "POST", unifiedOrder.toXml());
		if (result != null) {
			Map<String, String> resultMap = MessageUtil.parseXml(new ByteArrayInputStream(result.getBytes()));
			System.out.println(resultMap);
			UnifiedResult unifiedResult = new UnifiedResult();
			unifiedResult.fromMap(resultMap);
			return unifiedResult;
		}
		return null;
	}

	/**
	 * 申请退款，需要双向证书
	 * 
	 * @throws Exception
	 */
	public static RefundResponse sendRefund(RefundRequest refundRequest, InputStream certStream, String password)
			throws Exception {
		String url = ApiUrls.REFUND_ORDER_URL;
		System.out.println(refundRequest.toXml());
		String result = WeChatUtil.httpsRequestWithCert(url, "POST", refundRequest.toXml(), certStream, password);
		if (result != null) {
			Map<String, String> resultMap = MessageUtil.parseXml(new ByteArrayInputStream(result.getBytes()));
			System.out.println(resultMap);
			RefundResponse refundResult = new RefundResponse();
			refundResult.fromMap(resultMap);
			return refundResult;
		}
		return null;
	}

	/**
	 * 关闭订单
	 * 
	 * @throws Exception
	 */
	public static CloseResponse sendCloseRequest(CloseRequest closeRequest) throws Exception {
		String url = ApiUrls.CLOSE_ORDER_URL;
		System.out.println(closeRequest.toXml());
		String result = WeChatUtil.httpsRequest(url, "POST", closeRequest.toXml());
		if (result != null) {
			Map<String, String> resultMap = MessageUtil.parseXml(new ByteArrayInputStream(result.getBytes()));
			System.out.println(resultMap);
			CloseResponse closeResult = new CloseResponse();
			closeResult.fromMap(resultMap);
			return closeResult;
		}
		return null;
	}

	// https://mp.weixin.qq.com/mp/subscribemsg?action=get_confirm&appid=%s&scene=%s
	// &template_id=%s&redirect_url=%s&reserved=%s#wechat_redirect
	public static String getSubscribeMessageUrl(String appId, String scene, String templateId, String redirectUrl,
			String reserved) {
		return String.format(ApiUrls.SUBSCRIBE_MESSAGE_AUTH_URL, appId, scene, templateId, redirectUrl, reserved);
	}

	/**
	 * { “touser”:”OPENID”, “template_id”:”TEMPLATE_ID”, “url”:”URL”,
	 * “miniprogram”:{ “appid”:“xiaochengxuappid12345”, “pagepath”:“index?foo=bar”
	 * }, “scene”:”SCENE”, “title”:”TITLE”, “data”:{ “content”:{ “value”:”VALUE”,
	 * “color”:”COLOR” } } }
	 */
	/**
	 * touser 是 填接收消息的用户openid template_id 是 订阅消息模板ID url 否 点击消息跳转的链接，需要有ICP备案
	 * miniprogram 否 跳小程序所需数据，不需跳小程序可不用传该数据 appid 是
	 * 所需跳转到的小程序appid（该小程序appid必须与发模板消息的公众号是绑定关联关系，并且小程序要求是已发布的） pagepath 是
	 * 所需跳转到小程序的具体页面路径，支持带参数,（示例index?foo=bar） scene 是 订阅场景值 title 是 消息标题，15字以内 data
	 * 是 消息正文，value为消息内容文本（200字以内），没有固定格式，可用\n换行，color为整段消息内容的字体颜色（目前仅支持整段消息为一种颜色）
	 * 
	 * @throws Exception
	 */
	public static ResultMessage sendSubscribeMessageUrl(String accessToken, SubscribeMessage subscribeMessage)
			throws Exception {
		String url = String.format(ApiUrls.SEND_SUBSCRIBE_MESSAGE_URL, accessToken);
		String result = WeChatUtil.httpsRequest(url, "POST", subscribeMessage.getJsonString());
		System.out.println(result);
		if (result != null) {
			result = result.replace("msg_id", "msgId");
			ResultMessage resultMessage = JsonUtil.json2Bean(result, ResultMessage.class);
			return resultMessage;
		}
		return null;
	}

	/**
	 * 发放红包
	 */
	// https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack
	public static RedpacketResult sendRedpacket(RedpacketRequest redpack, InputStream certStream, //
			String password) throws Exception {
		String url = ApiUrls.SEND_REDPACK_URL;
		String result = WeChatUtil.httpsRequestWithCert(url, "POST", redpack.toXml(), //
				certStream, password);
		System.err.println(result);
		if (result != null) {
			Map<String, String> resultMap = MessageUtil.parseXml(result);
			System.out.println(resultMap);
			RedpacketResult redpackResult = new RedpacketResult();
			redpackResult.fromMap(resultMap);
			return redpackResult;
		}
		return null;
	}

	/**
	 * 
	 * @param args
	 * @throws JsonParseException
	 * @throws JsonMappingException
	 * @throws IOException
	 */

	public static void main(String[] args) throws Exception {
		// String result =
		// "{\"access_token\":\"ACCESS_TOKEN\",\"expires_in\":7200}".replace("access_token",
		// "token").replace("expires_in", "expiresIn");
		// AccessToken token = JsonUtil.json2Bean(result,
		// AccessToken.class);
		// System.out.println(token);

		// File file = new File("g:/HOME/旋动我心.jpg");
		// String token =
		// "dd4AanGVLjY0YVeZf0lqJVMUSiIb2w53V6ZUd5MSspEBFDLyPoDfgt7hM38CJvUmw46FdjyPMS9xUrA-R4GqngPf54awK7fHFnK6NTPIIio";
		// UploadMediaResult uploadMediaResult = uploadMedia(token,
		// file, MediaType.image.name());

		// System.out.println(uploadMediaResult);
		// String mediaId =
		// "3TRxgvjJjKxDza5PkcRNyvJPmUEGngRU3LzEeWSNVHKg7rdRpj4LDZKZ6TWrcb1k";
		// DownloadMediaResult downloadMediaResult = downMedia(token, mediaId);
		// FileOutputStream fileOutputStream = new FileOutputStream("g:/HOME/123.jpg");
		// BufferedInputStream bufferedInputStream =
		// downloadMediaResult.getInputStream();
		// IOUtils.copy(bufferedInputStream, fileOutputStream);
		// fileOutputStream.flush();
		// IOUtils.closeQuietly(fileOutputStream);
		// System.out.println(downloadMediaResult.getFileName());

		// String templateId = "NFO5rd1yCjwsZB68RyrSFXr6QehgLvuAubhiC--Btts";
		// String appId = "wx20c3792650764e5d";
		// String scene = "11";
		// String redirectUrl = URLEncoder.encode("http://admin.yidiangang.com");
		// String reserved = "yidiangang";
		// String authUrl = getSubscribeMessageUrl(appId, scene, templateId,
		// redirectUrl, reserved);
		// System.out.println(authUrl);

		String accessToken = "11_HDd3F3wOa-HzDEVC6WUnInDogM-7TIxO7apIUufCtrGUWytSwVny4M4eycC4uOIeukpEFWoQsCR1IAXzHePkql0KI2HyZc0d0FpK51yq7gmU3nEN1C9ZOSaseqbgAa5g5jAbjUrVgmUhMalQEYHeAJAXRA";
		SubscribeMessage subscribeMessage = new SubscribeMessage();
		subscribeMessage.setReceiver("og5m6w6UtTwE3_1RVVlpKSVN-9Tc");
		subscribeMessage.setScene("11");
		subscribeMessage.setTemplateId("NFO5rd1yCjwsZB68RyrSFXr6QehgLvuAubhiC--Btts");
		subscribeMessage.setTitle("快报通知");
		subscribeMessage.setContent(
				"您参加的紧急采购【太原交货；热轧卷板 3.75*1250*C Q235B或不限；产厂不限；仓库不限；需求2.000件；该信息两日有效】现已结束，采购买家最终未选择下单。感谢您的参与。");
		subscribeMessage.setColor("#173177");

		System.out.println(subscribeMessage.getJsonString());

		WeChatApi.sendSubscribeMessageUrl(accessToken, subscribeMessage);

	}

}
